package common.listener;

import common.CommonConstants;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * This listener checks whether the user is logged in before navigating
 * the user to the requested page. If the user isn't logged in, the listener 
 * will direct the user to the login page. 
 * 
 * NOTE: The listener is invoked on RESTORE_VIEW phase. It is, however, not
 * invoked on INVOKE_APPLICATION phase or other phases. Hence, security 
 * concerning application driven actions should be checked programmatically.
 */
public class AuthenticationPhaseListener implements PhaseListener {

    /**
     * <p>Determines if the user is authenticated.  If not, direct the
     * user to the login view, otherwise all the user to continue to the
     * requested view.</p>
     *
     * <p>Implementation Note: We do this in the <code>afterPhase</code>
     * to make use of the <code>NavigationHandler</code>.</p>
     */
    public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();

        if (!userExists(context)) {
            if (requestingSecureView(context)) {
                context.getApplication().getNavigationHandler().handleNavigation(context, null, "login");
            }
        }
    }

    /**
     * <p>This is a no-op.</p>
     */
    public void beforePhase(PhaseEvent event) {
    }

    /**
     * @return <code>PhaseId.RESTORE_VIEW</code>
     */
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
    // --------------------------------------------------------- Private Methods       
    /**
     * <p>Determine if the user has been authenticated by checking the session
     * for an existing <code>Wuser</code> object.</p>
     * 
     * @param context the <code>FacesContext</code> for the current request
     * @return <code>true</code> if the user has been authenticated, otherwise
     *  <code>false</code>
     */
    private boolean userExists(FacesContext context) {
        ExternalContext extContext = context.getExternalContext();
        return (extContext.getSessionMap().containsKey(CommonConstants.SSN_CUR_WEB_USER));
    }

    /**
     * <p>Determines if the requested view is of member restricted type, which requires
     * the user to be authenticated prior to accessing it.</p>
     *
     * @param context the <code>FacesContext</code> for the current request
     * @return <code>true</code> if the requested view is not to be accessed
     *  without being authenticated, otherwise <code>false</code>
     */
    private boolean requestingSecureView(FacesContext context) {
        String viewId = context.getViewRoot().getViewId();
        return !viewId.startsWith("/public/");
    }
}